package com.tyk.bookstore.back.auth.service.impl;

import com.tyk.bookstore.back.auth.manager.AuthRedisManager;
import com.tyk.bookstore.back.auth.model.query.CodeLoginQuery;
import com.tyk.bookstore.back.auth.model.query.PasswordLoginQuery;
import com.tyk.bookstore.back.auth.model.query.SendMsgCodeQuery;
import com.tyk.bookstore.back.auth.service.LoginService;
import com.tyk.bookstore.back.common.core.model.exception.CustomException;
import com.tyk.bookstore.back.common.core.model.result.CommonResultEnum;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.ComUtil;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.common.other.code.behavior.manager.BehaviorCodeManager;
import com.tyk.bookstore.back.common.other.code.behavior.model.enume.SceneType;
import com.tyk.bookstore.back.common.other.code.msg.manager.MsgCodeManager;
import com.tyk.bookstore.back.common.other.code.msg.model.enume.MsgCodeType;
import com.tyk.bookstore.back.common.security.util.PasswordUtil;
import com.tyk.bookstore.back.common.security.util.SecurityUtil;
import com.tyk.bookstore.back.system.model.dto.RoleDto;
import com.tyk.bookstore.back.system.model.dto.User4LoginCheckDto;
import com.tyk.bookstore.back.system.model.query.ExistsPrincipalQuery;
import com.tyk.bookstore.back.system.model.query.User4LoginCheckQuery;
import com.tyk.bookstore.back.system.model.result.UserResultEnum;
import com.tyk.bookstore.back.system.service.UserApiService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class LoginServiceImpl implements LoginService {

    private final UserApiService userApiService;

    private final BehaviorCodeManager behaviorCodeManager;
    private final MsgCodeManager msgCodeManager;
    private final AuthRedisManager authRedisManager;

    @Override
    public String loginByPwd(PasswordLoginQuery query) {

        // 校验行为验证码
        boolean f = behaviorCodeManager.check(SceneType.LOGIN, query.getCaptchaVerifyParam());
        AssertUtil.isTrue(f, CommonResultEnum.CODE_BEHAVIOR_ERROR);

        // 验证主体与凭证
        User4LoginCheckDto dto = null;
        User4LoginCheckQuery uq = new User4LoginCheckQuery().setPasswordRequired(true);
        switch (query.getType()) {
            case ACCOUNT -> {
                uq.setAccount(query.getPrincipal());
                dto = userApiService.selectUserByPrincipal(uq);
                AssertUtil.isTrue(
                        dto!=null&&PasswordUtil.check(query.getCredential(),dto.getPassword()),
                        UserResultEnum.ACCOUNT_OR_PASSWORD_ERROR
                );
            }
            case PHONE -> {
                uq.setPhone(query.getPrincipal());
                dto = userApiService.selectUserByPrincipal(uq);
                AssertUtil.isTrue(
                        dto!=null&&PasswordUtil.check(query.getCredential(),dto.getPassword()),
                        UserResultEnum.PHONE_OR_PASSWORD_ERROR
                );
            }
            case EMAIL -> {
                uq.setEmail(query.getPrincipal());
                dto = userApiService.selectUserByPrincipal(uq);
                AssertUtil.isTrue(
                        dto!=null&&PasswordUtil.check(query.getCredential(),dto.getPassword()),
                        UserResultEnum.EMAIL_OR_PASSWORD_ERROR
                );
            }
        }



        // 用户登录
        return doLogin(dto);
    }

    @Override
    public String sendMsgCode(SendMsgCodeQuery query) {

        // 校验行为验证码
        boolean f = behaviorCodeManager.check(SceneType.LOGIN, query.getCaptchaVerifyParam());
        AssertUtil.isTrue(f, CommonResultEnum.CODE_BEHAVIOR_ERROR);

        // 判断手机号、邮箱是否已注册,发送验证码
        String code = switch (query.getType()) {
            case EMAIL -> {
                // 邮箱已绑定
                ExistsPrincipalQuery pq = new ExistsPrincipalQuery().setEmail(query.getPrincipal());
                AssertUtil.isTrue(
                        userApiService.existsPrincipal(pq),
                        UserResultEnum.EMAIL_NOT_BOUND
                );
                // 发送邮箱验证码
                yield msgCodeManager.sendCodeAsync(MsgCodeType.EMAIL_QQ, query.getPrincipal());
            }
            case PHONE -> {
                // 邮箱已绑定
                ExistsPrincipalQuery pq = new ExistsPrincipalQuery().setPhone(query.getPrincipal());
                AssertUtil.isTrue(
                        userApiService.existsPrincipal(pq),
                        UserResultEnum.PHONE_NOT_BOUND
                );
                // 发送手机验证码
                yield msgCodeManager.sendCodeAsync(MsgCodeType.PHONE_ALI, query.getPrincipal());
            }
        };

        // 将验证码存入redis中
        String certification = ComUtil.uuid();
        authRedisManager.cacheLoginCertification(query.getPrincipal(), certification, code);
        return certification;

    }

    public String loginByCode(CodeLoginQuery query) {

        // 校验消息验证码
        String code = authRedisManager.getLoginCertification(query.getPrincipal(), query.getCodeCredential());
        AssertUtil.equal(
                code,
                query.getCredential(),
                switch (query.getType()) {
                    case PHONE -> CommonResultEnum.CODE_MSG_PHONE_ERROR_OR_EXPIRED;
                    case EMAIL -> CommonResultEnum.CODE_MSG_EMAIL_ERROR_OR_EXPIRED;
                }
        );

        // 查询用户信息
        User4LoginCheckQuery uq = new User4LoginCheckQuery().setPasswordRequired(false);
        switch (query.getType()) {
            case PHONE -> uq.setPhone(query.getPrincipal());
            case EMAIL -> uq.setEmail(query.getPrincipal());
        }
        User4LoginCheckDto dto = userApiService.selectUserByPrincipal(uq);

        // 删除消息验证码
        authRedisManager.deleteLoginCertification(query.getPrincipal(), query.getCodeCredential());

        // 用户登录
        return this.doLogin(dto);
    }

    /**
     * 执行登录操作
     *
     * @param user 用户信息
     * @return token
     */
    private String doLogin(User4LoginCheckDto user) {
        switch (user.getState()) {
            case ABLE -> {
                List<String> roleList = new ArrayList<>();
                List<String> permissionList = new ArrayList<>();
                if (user.getRoot()) {
                    roleList.add("*");
                    permissionList.add("*");
                } else {
                    List<RoleDto> roleDtoList = userApiService.selectRoleDtoList(user.getId());
                    roleList = roleDtoList.stream().map(RoleDto::getCode).toList();
                    if (ObjUtil.notEmpty(roleDtoList)) {
                        permissionList = userApiService
                                .selectPermissionList(
                                        roleDtoList.stream().map(RoleDto::getId).toList()
                                );
                    }
                }
                SecurityUtil.login(user.getId());
                SecurityUtil.setRoot(user.getRoot());
                SecurityUtil.setRoleList(roleList);
                SecurityUtil.setPermissionList(permissionList);
                return SecurityUtil.getTokenValue();
            }
            case DISABLE -> throw new CustomException(CommonResultEnum.USER_STATE_DISABLE);
            case CANCELED -> throw new CustomException(CommonResultEnum.USER_STATE_CANCELED);
            default -> throw new UnsupportedOperationException();
        }
    }

}
